/*
 * Copyright 2003-2007 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

package com.colony.channel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.argot.TypeException;
import com.argot.TypeInputStream;
import com.argot.TypeLibrary;
import com.argot.TypeMap;
import com.argot.TypeOutputStream;
import com.argot.common.BigEndianUnsignedInteger;
import com.argot.network.TypeEndPoint;

import com.colony.concurrent.ThreadActivator;
import com.colony.transport.Client;

public class ChannelClient
{
	private static int U32_ID = 1;
	private static int CHANNEL_ID = 2;

	private Client _client;
	private TypeLibrary _library;
	private ThreadActivator _threadActivator;
	
	private TypeMap _channelTypeMap;
	
	public ChannelClient( Client client, TypeLibrary library, ThreadActivator threadActivator ) 
	throws TypeException
	{
		_client = client;
		_library = library;
		_threadActivator = threadActivator;
		
		_channelTypeMap = new TypeMap( _library );
		_channelTypeMap.map(U32_ID, library.getId(BigEndianUnsignedInteger.TYPENAME));
		_channelTypeMap.map(CHANNEL_ID, library.getId(ChannelIdentifier.TYPENAME));
	}
	
	private Channel connectChannel( int id )
	throws IOException, TypeException
	{
		TypeEndPoint end = _client.openLink();
		InputStream in = end.getInputStream();
		OutputStream out = end.getOutputStream();
		
		// send the request ID. 0 = assign.
		TypeOutputStream tout = new TypeOutputStream( out, _channelTypeMap );
		tout.writeObject(CHANNEL_ID, new ChannelIdentifier(id));
		tout.getStream().flush();
		
		// get the response ID. 0 = connected.
		TypeInputStream tin = new TypeInputStream( in, _channelTypeMap );
		ChannelIdentifier cid = (ChannelIdentifier) tin.readObject(CHANNEL_ID);
				
		Channel channel = new Channel( in, out, cid );
		return channel;
	}

	// client is attempting to send a input or output stream to the server.
	public ChannelIdentifier connectSendStream( InputStream in, OutputStream out ) 
	throws IOException, TypeException
	{
		Channel channel = connectChannel(0);
		if ( channel.getId().getId() == 0 )
		{
			throw new TypeException("Channel not assigned.");
		}
		
		if ( in != null )
		{
			_threadActivator.assignThread( new StreamReader(in, channel.getOutputStream()));
		}
		
		if ( out != null )
		{
			_threadActivator.assignThread( new StreamReader(channel.getInputStream(), out));
		}
		
		return channel.getId();
	}
	
	// client is receiving an input or output stream from the server.
	public Channel connectReceiveStream( ChannelIdentifier id ) 
	throws IOException, TypeException
	{
		return connectChannel(id.getId());
	}
	
	private class StreamReader
	implements Runnable
	{
		private InputStream _in;
		private OutputStream _out;
		
		public StreamReader( InputStream in, OutputStream out)
		{
			_in = in;
			_out = out;
		}
		
		public void run()
		{
			try
			{
				int x;
				while( (x = _in.read()) > 0 )
				{
					_out.write(x);
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			
			try
			{
				_in.close();
			}
			catch (IOException e)
			{ 
				e.printStackTrace();
			}
			
			try
			{
				_out.close();
			}
			catch (IOException e)
			{ 
				e.printStackTrace();
			}
			
		}
	}
}
